/* 
 * Created by 2018年9月9日
 */
package com.edev.support.utils;

import com.edev.support.exception.ValidException;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

/**
 * 日期时间处理工具类（静态方法）
 * <p>
 * 核心功能：
 * 1. 日期与字符串相互转换（支持自定义格式）
 * 2. 时区处理（默认GMT+8）
 * 3. 日期范围计算（年/月/日维度）
 * <p>
 * 典型使用场景：
 * - 数据库日期字段与Java对象转换
 * - REST API中的日期参数处理
 * - 报表统计中的日期范围计算
 * <p>
 * 注意事项：
 * - 默认时区为GMT+8（中国时区）
 * - 默认日期格式为yyyy-MM-dd HH:mm:ss
 */
public class DateUtils {
	private DateUtils() {}
	private static final String TIMEZONE="GMT+8";
	private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
	private static final TimeZone TIME_ZONE = TimeZone.getTimeZone(TIMEZONE);

	/**
	 * 获取指定日期的日历实例（带时区）
	 * @param date 目标日期
	 * @return 配置了GMT+8时区的Calendar对象
	 */
	public static Calendar getCalendar(Date date){
		Calendar calendar = getCalendar();
		calendar.setTime(date);
		return calendar;
	}

	/**
	 * 获取当前时间的日历实例（带时区）
	 * @return the calendar of now
	 */
	public static Calendar getCalendar() {
		return Calendar.getInstance(TIME_ZONE);
	}
	
	/**
	 * 获取当前时间的日期对象（带时区）
	 * @return 当前时间Date对象
	 */
	public static Date getNow() {
		return getCalendar().getTime();
	}

	/**
	 * 字符串转日期对象（根据自定义日期格式）
	 * @param string 日期字符串
	 * @param format 日期格式（如：yyyy-MM-dd）
	 * @return 解析后的Date对象
	 * @throws ValidException 日期格式不匹配时抛出
	 */
	public static Date getDate(String string, String format) {
		SimpleDateFormat sdf = new SimpleDateFormat(format);
		sdf.setTimeZone(TIME_ZONE);
		try {
			return sdf.parse(string);
		} catch (ParseException e) {
			throw new ValidException("fail to format date: "+string);
		}
	}

	/**
	 * 字符串转日期对象（根据默认日期格式）
	 * @param string 日期字符串
	 * @return 解析后的Date对象
	 * @throws ValidException 日期格式不匹配时抛出
	 */
	public static Date getDate(String string) {
		return getDate(string, DATE_FORMAT);
	}

	/**
	 * 获取指定的日期对象（年/月/日）
	 * @param year the year
	 * @param month the month
	 * @param date the date
	 * @return the date
	 */
	public static Date getDate(int year, int month, int date) {
		Calendar calendar = getCalendar();
		calendar.set(year, month, date);
		return calendar.getTime();
	}

	/**
	 * 将字符串转换为UTC格式的日期对象（用于处理UTC时间）
	 * @param string UTC日期字符串（如：2024-09-09T12:00:00Z）
	 * @return 解析后的Date对象
	 */
	public static Date getDateForUTC(String string) {
		string = string.replace("Z", " UTC");
		String format = "";
		if(string.length()==16) format = "yyyy-MM-dd'T'HH:mm";
		else if(string.length()==19) format = "yyyy-MM-dd'T'HH:mm:ss";
		else format = "yyyy-MM-dd'T'HH:mm:ss.SSS Z";
		return getDate(string, format);
	}

	/**
	 * 将日期对象转换为字符串（根据自定义日期格式）
	 * @param date 目标日期
	 * @param format 日期格式（如：yyyy-MM-dd）
	 * @return 格式化后的日期字符串
	 */
	public static String getStringOfDate(Date date, String format){
		SimpleDateFormat sdf = new SimpleDateFormat(format);
		sdf.setTimeZone(TIME_ZONE);
		return sdf.format(date);
	}

	/**
	 * 将日期对象转换为字符串（根据默认日期格式）
	 * @param date 目标日期
	 * @return 格式化后的日期字符串
	 */
	public static String getStringOfDate(Date date){
		return getStringOfDate(date, DATE_FORMAT);
	}

	/**
	 * 获取指定日期的年份
	 * @param date 目标日期
	 * @return 指定日期的年份
	 */
	public static int getYear(Date date){
		return getCalendar(date).get(Calendar.YEAR);
	}

	/**
	 * 获取当前年份
	 * @return this year
	 */
	public static int getThisYear(){
		return getYear(getNow());
	}

	/**
	 * 获取指定日期所在年份的第一天
	 * @param date 目标日期
	 * @return 该年份的第一天
	 */
	public static Date getFirstDayOfYear(Date date){
		Calendar calendar = getCalendar(date);
		calendar.set(Calendar.MONTH, Calendar.JANUARY);
		calendar.set(Calendar.DAY_OF_MONTH, 1);
		return calendar.getTime();
	}

	/**
	 * 获取指定日期所在年份的最后一天
	 * @param date 目标日期
	 * @return 该年份的最后一天
	 */
	public static Date getLastDayOfYear(Date date){
		Calendar calendar = getCalendar(date);
		calendar.set(Calendar.MONTH, Calendar.DECEMBER);
		calendar.set(Calendar.DAY_OF_MONTH, 31);
		return calendar.getTime();
	}

	/**
	 * 获取两个日期之间的年份差
	 * @param firstDate 起始日期
	 * @param lastDate 截止日期
	 * @return 两个日期之间的年份差（考虑跨年计算）
	 */
	public static int getYearsBetween(Date firstDate, Date lastDate){
		return getYear(lastDate) - getYear(firstDate);
	}

	/**
	 * 获取指定日期的月份
	 * @param date 目标日期
	 * @return 指定日期的月份
	 */
	public static int getMonth(Date date){
		return getCalendar(date).get(Calendar.MONTH)+1;
	}

	/**
	 * 获取当前月份
	 * @return this month
	 */
	public static int getThisMonth(){
		return getMonth(getNow());
	}

	/**
	 * 获取某月的第一天
	 * @param date 目标日期
	 * @return 该月第一天的Date对象
	 */
	public static Date getFirstDayOfMonth(Date date){
		Calendar calendar = getCalendar(date);
		int year = calendar.get(Calendar.YEAR);
		int month = calendar.get(Calendar.MONTH);
		calendar.set(year, month, 1);
		return calendar.getTime();
	}

	/**
	 * 获取某月的最后一天
	 * @param date 目标日期
	 * @return 该月最后一天的Date对象
	 */
	public static Date getLastDayOfMonth(Date date){
		Calendar calendar = getCalendar(date);
		calendar.add(Calendar.MONTH, 1);
		Date theFirstDayOfNextMonth = getFirstDayOfMonth(calendar.getTime());
		calendar = getCalendar(theFirstDayOfNextMonth);
		calendar.add(Calendar.DAY_OF_MONTH, -1);
		return calendar.getTime();
	}

	/**
	 * 计算两个日期之间的月份差
	 * @param firstDate 起始日期
	 * @param lastDate 结束日期
	 * @return 相差的月份数（考虑跨年计算）
	 */
	public static int getMonthsBetween(Date firstDate, Date lastDate){
		int yearsBetween = getYearsBetween(firstDate, lastDate);
		Calendar calendar = getCalendar(lastDate);
		calendar.set(Calendar.YEAR, getYear(firstDate));
		int monthsBetween = getMonth(calendar.getTime()) - getMonth(firstDate);
		return yearsBetween*12 + monthsBetween;
	}

	/**
	 * 计算两个日期之间的天数差
	 * @param firstDate 起始日期
	 * @param lastDate 结束日期
	 * @return 相差的月份数（考虑跨年计算）
	 */
	public static long getDatesBetween(Date firstDate, Date lastDate){
		Calendar calendar1 = getCalendar(firstDate);
		Calendar calendar2 = getCalendar(lastDate);
		long between = calendar2.getTimeInMillis()-calendar1.getTimeInMillis();
		return between/(24*60*60*1000);
	}
}
